package models;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import java.util.Vector;

import javax.swing.table.DefaultTableModel;

import org.apache.log4j.Logger;

import util.ResultTable;
import util.SystemUtil;

import frame.FeatureHandler;


/**
 * @author Rishi Das Roy
 *
 * @Organization Institute Of Genomics & Integrative Biology
 */
public class ResultByReverseTree {
	private static Logger log = Logger.getLogger(ResultByReverseTree.class);

	public static ResultTable decision(BaseModel[] predModel, String filePath, FeatureHandler lfh) throws IOException {

		Vector columnNames = new Vector();
		Vector data = new Vector();

		BaseModel KGS1_Non = null;
		BaseModel KGS2_Non = null;
		BaseModel KGS3_Non = null;
		BaseModel FGS4_KGS3Minus = null;

		for (BaseModel model : predModel) {
			if(model.getName().equals("KGS1_Non")){
				 KGS1_Non = model;
			}else if(model.getName().equals("KGS2_Non")){
				 KGS2_Non = model;
			}else if(model.getName().equals("KGS3_Non")){
				 KGS3_Non = model;
			}else if(model.getName().equals("FGS4_KGS3Minus")){
				 FGS4_KGS3Minus = model;
			}
		}

		BufferedWriter lBw = new BufferedWriter(new FileWriter(filePath+"_Result.csv"));
		lBw.write("Input Class,Prediction,Score,prot_id"+System.getProperty("line.separator"));
		columnNames.addElement("prot_id");
		columnNames.addElement("Prediction");
		columnNames.addElement("Score");

		for (int i = 0; i < lfh.getActualLabels().size(); i++) {
			int actualLabel =  lfh.getActualLabels().get(i);
			float voteScore = 1;
			String lPredictLabel = null;
			double probabilityScore = 1;

			if( KGS1_Non.getResult().getPredictedLabels().get(i)>=0){
				lPredictLabel = KGS1_Non.getPosLabel();
				probabilityScore = probabilityScore*KGS1_Non.getResult().getPredictedLabels().get(i);
			}else if(KGS2_Non.getResult().getPredictedLabels().get(i)>=0){
				lPredictLabel = KGS2_Non.getPosLabel();
				probabilityScore = probabilityScore*KGS1_Non.getResult().getPredictedLabels().get(i)
					*KGS2_Non.getResult().getPredictedLabels().get(i);
			}else if(KGS3_Non.getResult().getPredictedLabels().get(i)<=0){
				/***/
				lPredictLabel = KGS3_Non.getNegLabel();
				probabilityScore = probabilityScore*KGS1_Non.getResult().getPredictedLabels().get(i)
				*KGS2_Non.getResult().getPredictedLabels().get(i)
				*KGS3_Non.getResult().getPredictedLabels().get(i);
				voteScore = -1;
			}else if(FGS4_KGS3Minus.getResult().getPredictedLabels().get(i)>=0){
				lPredictLabel = FGS4_KGS3Minus.getPosLabel();
				probabilityScore = probabilityScore*KGS1_Non.getResult().getPredictedLabels().get(i)
				*KGS2_Non.getResult().getPredictedLabels().get(i)
				*KGS3_Non.getResult().getPredictedLabels().get(i)
				*FGS4_KGS3Minus.getResult().getPredictedLabels().get(i);
			}else {
				lPredictLabel = FGS4_KGS3Minus.getNegLabel();
				probabilityScore = probabilityScore*KGS1_Non.getResult().getPredictedLabels().get(i)
				*KGS2_Non.getResult().getPredictedLabels().get(i)
				*KGS3_Non.getResult().getPredictedLabels().get(i)
				*FGS4_KGS3Minus.getResult().getPredictedLabels().get(i);
			}


			lBw.write(actualLabel+","+lPredictLabel+","+Math.abs(probabilityScore)+","+lfh.getId().get(i)+System.getProperty("line.separator"));
			Vector row = new Vector();
			row.addElement(lfh.getId().get(i));
			row.addElement(lPredictLabel);
			row.addElement(SystemUtil.roundTwoDecimals(Math.abs(probabilityScore)));

			data.addElement(row);

			if(voteScore*actualLabel>0){
				if(voteScore>0){
					mTruePositive++;
				}else{
					mTrueNegative++;
				}

			}
		}
		lBw.close();
		mFalsePositive = lfh.getNegativeCounts() - mTrueNegative;
		mFalseNegative = lfh.getPositiveCounts() - mTruePositive;
		mTotal = lfh.getActualLabels().size();

		writeResult(filePath+"_performance.csv");
		DefaultTableModel model = new DefaultTableModel(data, columnNames);
		ResultTable lRT = new ResultTable(model);
		lRT.setName("Reverse Decision Tree");
		return lRT;

	}




	private static long mTruePositive = 0;
	private static long mTrueNegative = 0;
	private List<Float> mPredictedLabels;
	private static long mFalsePositive;
	private static long mFalseNegative;
	private static long mTotal;

	public long getFalseNegative() {
		return mFalseNegative;
	}

	public long getFalsePositive() {
		return mFalsePositive;
	}

	public List<Float> getPredictedLabels() {
		return mPredictedLabels;
	}

	public long getTrueNegative() {
		return mTrueNegative;
	}

	public long getTruePositive() {
		return mTruePositive;
	}

	public static void writeResult(String pFilePath) throws IOException {
		BufferedWriter lBw = new BufferedWriter(new FileWriter(pFilePath));
		lBw.write("Confusion Matrix"+System.getProperty("line.separator"));
		lBw.write(",,Actual"+System.getProperty("line.separator"));
		lBw.write(",,GS,NGS,Total"+System.getProperty("line.separator"));
		lBw.write("Predicted,GS,"+mTruePositive+","+mFalsePositive+","+(mTruePositive+mFalsePositive)+System.getProperty("line.separator"));
		lBw.write("Predicted,NGS,"+mFalseNegative+","+mTrueNegative+","+(mTrueNegative+mFalseNegative)+System.getProperty("line.separator"));
		lBw.write(",Total,"+(mTruePositive+mFalseNegative)+","+(mTrueNegative+mFalsePositive)+System.getProperty("line.separator"));

		lBw.write("Accuracy ,"+getAccuracy()+System.getProperty("line.separator"));
		lBw.write("Balance Accuracy ,"+getBAC()+System.getProperty("line.separator"));
		lBw.write("MCC ,"+getMcc()+System.getProperty("line.separator"));
		lBw.write("Class,GS,NGS"+System.getProperty("line.separator"));
		lBw.write("Recall ,"+getP_Recall()+","+getN_Recall()+System.getProperty("line.separator"));
		lBw.write("Precision ,"+getP_Precision()+","+getN_Precision()+System.getProperty("line.separator"));
		lBw.write("F-Measure ,"+getP_Fmeasure()+","+getN_Fmeasure()+System.getProperty("line.separator"));
		lBw.close();
	}

	private static float getBAC() {

		return (getP_Recall()+getN_Recall())/2;
	}

	private static double getMcc() {
		double square_root = Math.sqrt((mTruePositive+mFalsePositive)*(mTruePositive+mFalseNegative)
					*(mTrueNegative+mFalsePositive)*(mTrueNegative+mFalseNegative));
		if(square_root<=0)
			return 0;


		return (mTruePositive*mTrueNegative - mFalsePositive*mFalseNegative)/square_root;
	}

	private static float getAccuracy() {
		return (float)(mTruePositive+mTrueNegative)/mTotal;
	}

	private static float getN_Fmeasure() {
		float denominator = (getN_Precision()+getN_Recall());
		if(denominator<=0)
			return 0;


		return ((float)2*getN_Precision()*getN_Recall()/denominator);
	}

	private static float getP_Fmeasure() {
		float denominator = (getP_Precision()+getP_Recall());
		if(denominator<=0)
			return 0;


		return ((float)2*getP_Precision()*getP_Recall()/denominator);
	}

	private static float getN_Precision() {
		long denominator = (mTrueNegative+mFalseNegative);
		if(denominator<=0)
			return 0;


		return ((float)mTrueNegative/denominator);
	}

	private static float getP_Precision() {
		long denominator = (mTruePositive + mFalsePositive);
		if(denominator<=0)
			return 0;


		return ((float)mTruePositive/denominator);
	}

	private static float getN_Recall() {
		long denominator = (mTrueNegative + mFalsePositive);
		if(denominator<=0)
			return 0;


		return ((float)mTrueNegative/denominator);
	}

	/**
	 * Sensitivity;Recall;
	 * true positive rate, hit rate
	 * @return
	 */
	private static float getP_Recall() {
		long denominator = (mTruePositive + mFalseNegative);
		if(denominator<=0)
			return 0;

		return ((float)mTruePositive/denominator);
	}

}

